package com.sugon.modules.analyse.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.sugon.dao.ModuleRepairRocordDao;
import com.sugon.dao.ModuleSpStockDao;
import com.sugon.dao.ModuleTimesRecordProdDao;
import com.sugon.dao.MouldAnalyseDao;
import com.sugon.entity.ModuleRepairRocordEntity;
import com.sugon.modules.analyse.model.BarChartOptionModel;
import com.sugon.modules.analyse.model.BarChartSeriesModel;
import com.sugon.modules.analyse.model.chart.LineChartDataModel;
import com.sugon.modules.analyse.model.chart.PieChartOptionDataVO;
import com.sugon.modules.analyse.model.chart.PieChartSeriesDataItemVO;
import com.sugon.modules.analyse.model.dto.MoldFaultRateStatResultDTO;
import com.sugon.modules.analyse.model.vo.FaultTopStatVO;
import com.sugon.modules.analyse.model.vo.MoldFaultRateStatVO;
import com.sugon.modules.analyse.model.vo.WorkshopRepairStatVO;
import com.sugon.modules.analyse.service.IMouldAnalyseService;
import com.sugon.modules.mold.consts.MoldState;
import com.sugon.service.ModuleTypeService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @description: 磨具分析统计Service
 * @author: YuXD
 * @create: 2020-05-10 19:56
 **/
@Service
public class MouldAnalyseServiceImpl implements IMouldAnalyseService {

    @Resource
    private MouldAnalyseDao mouldAnalyseDao;
    @Resource
    private ModuleTypeService moduleTypeService;
    @Resource
    private ModuleRepairRocordDao moldRepairRecordDao;
    @Resource
    private ModuleSpStockDao moduleSpStockDao;
    @Resource
    private ModuleTimesRecordProdDao moduleTimesRecordDao;

    @Override
    public MoldFaultRateStatVO statMoldFaultRate(String workshopId, String moldTypeId) {

        MoldFaultRateStatVO statResult = new MoldFaultRateStatVO();

        // 获取近6个月的月份列表
        List<String> lastMonthRangeList = com.sugon.modules.utils.DateUtil.getLastNormMonthRangeList(6);
        statResult.setMonthlyList(lastMonthRangeList);

        // 获取模具类型ID列表，包含模具类型父类和子类的
        List<String> mouldTypeList = moduleTypeService.getModuleTypeIdList(moldTypeId);
        // 月度模具采集次数
        List<MoldFaultRateStatResultDTO> moldTimesCollectTimesStat = moduleTimesRecordDao.doMoldTimesMonthlyCountStat(workshopId, mouldTypeList, lastMonthRangeList);
        if (moldTimesCollectTimesStat == null) {
            moldTimesCollectTimesStat = CollectionUtil.newArrayList();
        }
        Map<String, List<MoldFaultRateStatResultDTO>> monthlyMoldTimesCollectTimesStat = moldTimesCollectTimesStat.stream().collect(Collectors.groupingBy(MoldFaultRateStatResultDTO::getMonthly));

        // 获取月度模具故障时长统计
        List<MoldFaultRateStatResultDTO> moldFaultRateStat = moldRepairRecordDao.doMoldFaultDurationMonthlyStat(workshopId, mouldTypeList, lastMonthRangeList);
        if (moldFaultRateStat == null) {
            moldFaultRateStat = CollectionUtil.newArrayList();
        }
        Map<String, List<MoldFaultRateStatResultDTO>> monthlyMoldFaultDurationStat = moldFaultRateStat.stream().collect(Collectors.groupingBy(MoldFaultRateStatResultDTO::getMonthly));

        // 分组计算
        for (String monthly : lastMonthRangeList) {
            // 如果某个月未生产 或者 没有故障记录，那么默认为0
            List<MoldFaultRateStatResultDTO> monthlyMoldTimesCollectRecords = monthlyMoldTimesCollectTimesStat.get(monthly);
            List<MoldFaultRateStatResultDTO> monthlyMoldFaultDurationRecords = monthlyMoldFaultDurationStat.get(monthly);
            if (CollectionUtil.isEmpty(monthlyMoldTimesCollectRecords) || CollectionUtil.isEmpty(monthlyMoldFaultDurationRecords)) {
                statResult.addMoldMonthlyFaultRate(BigDecimal.ZERO);
                continue;
            }
            // 有效运行时长 = 有记录的模具数量*一天的分钟数
            int validRuntimeDuration = monthlyMoldTimesCollectRecords.get(0).getTotalCount() * 1440;
            // 故障时长
            int faultDuration = monthlyMoldFaultDurationRecords.get(0).getTotalCount();
            statResult.addMoldMonthlyFaultRate(NumberUtil.mul(NumberUtil.div(faultDuration, validRuntimeDuration, 4), BigDecimal.valueOf(100)));
        }
        return statResult;
    }

    @Override
    public LineChartDataModel statMoldTypeMonthlyCostChange(String workshopId) {


        // TODO


        return null;
    }

    @Override
    public WorkshopRepairStatVO moldRepairStat(String workshopId) {
        WorkshopRepairStatVO repairStat = new WorkshopRepairStatVO();
        // 获取本日报修列表
        Date today = DateUtil.date();
        String todayStartDate = DateUtil.formatDateTime(DateUtil.beginOfDay(today));
        String todayEndDate = DateUtil.formatDateTime(DateUtil.endOfDay(today));
        Map<String, Object> queryParam = MapUtil.newHashMap();
        queryParam.put("workShop", workshopId);
        queryParam.put("startDate", todayStartDate);
        queryParam.put("endDate", todayEndDate);
        List<ModuleRepairRocordEntity> todayUploadList = moldRepairRecordDao.queryList(queryParam);
        if (todayUploadList == null) {
            todayUploadList = CollectionUtil.newArrayList();
        }
        repairStat.setUploadNum(todayUploadList.size());
        repairStat.setUploadList(todayUploadList);
        // 设置本日完成数
        repairStat.setCompleteNum(moldRepairRecordDao.countTodayCompleteNum(workshopId, todayStartDate, todayEndDate));
        return repairStat;
    }

    /**
     * getFaultDurationStat
     *
     * @param param
     * @return
     */
    @Override
    public BarChartOptionModel getFaultDurationStat(Map<String, Object> param) {
        // 移除空参数
        com.sugon.utils.MapUtil.removeEmptyValue(param);
        // 获取模具类型列表,并放置到查询参数汇总
        List<String> mouldTypeList = moduleTypeService.getModuleTypeIdList(MapUtil.getStr(param, "mouldTypeId"));
        if (CollectionUtil.isNotEmpty(mouldTypeList)) {
            param.put("mouldTypeList", mouldTypeList);
        }
        // 初始化返回数据
        BarChartOptionModel faultDurationStatChartModel = new BarChartOptionModel();
        // 故障时间
        BarChartSeriesModel<Integer> faultTimeBar = new BarChartSeriesModel<>();
        // 故障处理时间
        BarChartSeriesModel<Integer> faultHandleTimeBar = new BarChartSeriesModel<>();
        // 获取模具故障时长分析数据
        List<Map<String, Object>> faultDurationStatList = mouldAnalyseDao.getFaultDurationStat(param);
        // 判断数据是否为空
        if (CollectionUtil.isNotEmpty(faultDurationStatList)) {
            for (Map<String, Object> faultDurationStat : faultDurationStatList) {
                String moduleNo = MapUtil.getStr(faultDurationStat, "MODULE_NO");
                if (StrUtil.isEmpty(moduleNo)) {
                    continue;
                }
                faultDurationStatChartModel.addXAxisData(moduleNo);
                int faultTime = com.sugon.utils.MapUtil.getInt(faultDurationStat, "FAULTTIME", 0);
                faultTimeBar.add(faultTime);
                faultHandleTimeBar.add(com.sugon.utils.MapUtil.getInt(faultDurationStat, "WEIXIUTIME", 0));
            }
        }
        faultDurationStatChartModel.addBarSeriesData(faultTimeBar);
        faultDurationStatChartModel.addBarSeriesData(faultHandleTimeBar);
        return faultDurationStatChartModel;
    }

    /**
     * statMouldStatus
     *
     * @return
     */
    @Override
    public PieChartOptionDataVO statMouldStatus(String workshopId) {
        PieChartOptionDataVO optionData = new PieChartOptionDataVO();
        // 获取模具状态列表
        MoldState[] mouldStatuses = MoldState.values();
        // 1、获取模具状态统计
        Map<Integer, Integer> mouldStatusStat = getMouldStatusStat(workshopId);
        for (MoldState mouldStatus : mouldStatuses) {
            optionData.addLegendData(mouldStatus.getDesc());
            optionData.addSeriesData(new PieChartSeriesDataItemVO(mouldStatus.getDesc(), mouldStatusStat.get(mouldStatus.getState())));
        }
        return optionData;
    }

    /**
     * faultClassifyTimesStat
     *
     * @param param 包含月份、模具类别ID、所属铸造车间ID
     * @return
     */
    @Override
    public FaultTopStatVO faultClassifyTimesStat(Map<String, Object> param) {
        // 移除空参数
        com.sugon.utils.MapUtil.removeEmptyValue(param);
        // 获取模具类型列表,并放置到查询参数汇总
        List<String> mouldTypeList = moduleTypeService.getModuleTypeIdList(MapUtil.getStr(param, "mouldTypeId"));
        if (CollectionUtil.isNotEmpty(mouldTypeList)) {
            param.put("mouldTypeList", mouldTypeList);
        }
        FaultTopStatVO faultTopStatData = new FaultTopStatVO();
        //  获取模具故障分类
        List<Map<String, Object>> mouldFaultClassifyTimesStatDataList = moldRepairRecordDao.mouldFaultClassifyTimesTopStat(param);
        if (CollectionUtil.isNotEmpty(mouldFaultClassifyTimesStatDataList)) {
            for (Map<String, Object> mouldFaultClassifyTimesStatData : mouldFaultClassifyTimesStatDataList) {
                faultTopStatData.add(MapUtil.getStr(mouldFaultClassifyTimesStatData, "TYPE_NAME"), MapUtil.getInt(mouldFaultClassifyTimesStatData, "TIMES"));
            }
        }
        return faultTopStatData;
    }

    /**
     * faultTimesTopStat
     *
     * @param param 包含查询起止时间段和TOP数
     * @return
     */
    @Override
    public FaultTopStatVO faultTimesTopStat(Map<String, Object> param) {
        FaultTopStatVO faultTopStatData = new FaultTopStatVO();
        // 获取模具类型列表,并放置到查询参数汇总
        List<String> mouldTypeList = moduleTypeService.getModuleTypeIdList(MapUtil.getStr(param, "mouldTypeId"));
        if (CollectionUtil.isNotEmpty(mouldTypeList)) {
            param.put("mouldTypeList", mouldTypeList);
        }
        // 获取模具故障频次top
        List<Map<String, Object>> mouldFaultTimesTopStatDataList = moldRepairRecordDao.mouldFaultTimesTopStat(param);
        if (CollectionUtil.isNotEmpty(mouldFaultTimesTopStatDataList)) {
            for (Map<String, Object> mouldFaultTimesTopStatData : mouldFaultTimesTopStatDataList) {
                faultTopStatData.add(MapUtil.getStr(mouldFaultTimesTopStatData, "MODULENO"), MapUtil.getInt(mouldFaultTimesTopStatData, "TIMES"));
            }
        }
        return faultTopStatData;
    }

    /**
     * doMoldCostStat
     *
     * @param param 包含年份、模具类别ID、所属铸造车间ID
     * @return
     */
    @Override
    public List<List<Object>> doMoldCostStat(Map<String, Object> param) {
        List<List<Object>> moldCostStatDataResult = new ArrayList<>();
        /*
            如果年份参数为空，那么默认展示近几年(一般指有记录年份到现在的时间)的
            否则，展示指定年度每月的成本统计信息
            [
                ['dateRange', '2012', '2013', '2014', '2015', '2016', '2017'],
                ['Milk Tea', 56.5, 82.1, 88.7, 70.1, 53.4, 85.1],
                ['Matcha Latte', 51.1, 51.4, 55.1, 53.3, 73.8, 68.7],
                ['Cheese Cocoa', 40.1, 62.2, 69.5, 36.4, 45.2, 32.5],
                ['Walnut Brownie', 25.2, 37.1, 41.2, 18, 33.9, 49.1]
            ]
         */
        String yearly = MapUtil.getStr(param, "year");
        // 获取模具成本统计日期范围
        List<Object> moldCostStatDateRange = buildMoldCostStatDateRange(yearly, param);
        if (CollectionUtil.isEmpty(moldCostStatDateRange)) {
            return moldCostStatDataResult;
        }
        // 依次添加模具每个日期范围内的成本统计

        moldCostStatDataResult = buildMoldCostStatData(param, moldCostStatDateRange);
        moldCostStatDateRange.add(0, "dateRange");
        moldCostStatDataResult.add(0, moldCostStatDateRange);
        return moldCostStatDataResult;
    }

    /**
     * 构建模具成本统计日期范围
     * 如果yearly为空，那么返回['2012','2013','2014','2015','2016']
     * 否则返回['2012-01','2012-02']
     *
     * @param yearly
     * @return
     */
    private List<Object> buildMoldCostStatDateRange(String yearly, Map<String, Object> param) {
        List<Object> dateRange = new ArrayList<>();
        if (StrUtil.isEmpty(yearly)) {
            // 获取有记录的最早年份
            String earliestUsedYearlyVal = moduleSpStockDao.getEarliestUsedYearly();
            if (StrUtil.isEmpty(earliestUsedYearlyVal)) {
                return dateRange;
            }
            int currentYear = DateUtil.thisYear();
            int earliestUsedYearly = Integer.parseInt(earliestUsedYearlyVal);
            for (int i = earliestUsedYearly; i <= currentYear; i++) {
                dateRange.add(i + "");
            }
            // 设置查询中的groupby关键字
            param.put("groupByKey", "'yyyy'");
        } else {
            int currentMonth = DateUtil.thisMonth() + 1;
            for (int i = 1; i <= currentMonth; i++) {
                dateRange.add(yearly + "-" + fillZero(i));
            }
            // 设置查询中的groupby关键字
            param.put("groupByKey", "'yyyy-MM'");
        }
        return dateRange;
    }

    /**
     * 构建模具成本统计数据
     *
     * @param param                 查询参数
     * @param moldCostStatDateRange 模具成本统计日期范围，可能为['2012','2013','2014','2015','2016'] 或者为['2012-01','2012-02']
     * @return
     */
    private List<List<Object>> buildMoldCostStatData(Map<String, Object> param, List<Object> moldCostStatDateRange) {
        List<List<Object>> moldCostStatData = new ArrayList<>();
        // 获取模具类型列表,并放置到查询参数汇总
        List<String> mouldTypeList = moduleTypeService.getModuleTypeIdList(MapUtil.getStr(param, "mouldTypeId"));
        if (CollectionUtil.isNotEmpty(mouldTypeList)) {
            param.put("mouldTypeList", mouldTypeList);
        }
        // 获取模具成本统计数据
        List<Map<String, Object>> moldCostStatMapList = moldRepairRecordDao.moldCostStat(param);
        /*
            1、将从DB中查询出来的数据整理成{'Z2-YZ-NE15-2':{'2021-06':160,'2021-07':220},'Z2-GG-NE14-1':{}}
            以模具号为键，每月和对应月份的统计为值
         */
        Map<String, Map<String, Object>> temMap = new LinkedHashMap<>();
        String daterange, moduleNo;
        for (Map<String, Object> moldCostStatMap : moldCostStatMapList) {
            moduleNo = MapUtil.getStr(moldCostStatMap, "MODULE_NO");
            if (!temMap.containsKey(moduleNo)) {
                temMap.put(moduleNo, new HashMap<>());
            }
            daterange = MapUtil.getStr(moldCostStatMap, "DATERANGE");
            if (StrUtil.isEmpty(daterange)) {
                continue;
            }
            temMap.get(moduleNo).put(daterange, MapUtil.getDouble(moldCostStatMap, "COST"));
        }
        /*

        ['Milk Tea', 56.5, 82.1, 88.7, 70.1, 53.4, 85.1],
                ['Matcha Latte', 51.1, 51.4, 55.1, 53.3, 73.8, 68.7],
                ['Cheese Cocoa', 40.1, 62.2, 69.5, 36.4, 45.2, 32.5],
                ['Walnut Brownie', 25.2, 37.1, 41.2, 18, 33.9, 49.1]
         */
        Set<Map.Entry<String, Map<String, Object>>> entries = temMap.entrySet();
        Map<String, Object> dataRangeMoldCostStat;
        for (Map.Entry<String, Map<String, Object>> moldCostStatEntry : entries) {
            moduleNo = moldCostStatEntry.getKey();
            dataRangeMoldCostStat = moldCostStatEntry.getValue();
            List<Object> moldCostStatList = new ArrayList<>();
            moldCostStatList.add(moduleNo);
            for (Object dateRange : moldCostStatDateRange) {
                moldCostStatList.add(com.sugon.modules.utils.MapUtil.getDouble(dataRangeMoldCostStat, dateRange.toString(), 0));
            }
            moldCostStatData.add(moldCostStatList);
        }
        return moldCostStatData;
    }

    /**
     * 如果月份小于10，那么在前面自动补0
     *
     * @param month
     * @return
     */
    private String fillZero(int month) {
        return (month < 10 ? "0" : "") + month;
    }

    /**
     * 获取模具状态统计
     *
     * @return 返回以模具状态为key, 次数为value的Map
     */
    private Map<Integer, Integer> getMouldStatusStat(String workshopId) {
        Map<Integer, Integer> mouldStatusStat = new HashMap<>();
        // 获取模具状态数据，判断数据是否为空
        List<Map<String, Object>> statusStatList = mouldAnalyseDao.mouldStateStat(workshopId);
        if (CollectionUtil.isNotEmpty(statusStatList)) {
            for (Map<String, Object> statusStat : statusStatList) {
                mouldStatusStat.put(MapUtil.getInt(statusStat, "STATE"), MapUtil.getInt(statusStat, "NUM"));
            }
        }
        return mouldStatusStat;
    }

}
